﻿/*
    This file is part of News Worm.

    News Worm is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    News Worm is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with News Worm.  If not, see <http://www.gnu.org/licenses/>.
 */

using System;
using System.Collections.Generic;
using Microsoft.CSharp;
using System.CodeDom.Compiler;
using System.Windows.Forms;
using System.Reflection;
using System.IO;

namespace NewsWorm {
    class ScriptManager : IScriptManager {

        //parses the scripts and checks from status.dat file if the script is enabled or not.
        public ScriptManager( IScriptingEvents events ) {
            m_Scripts = new List<Script>();
            m_Events = events;
            m_InstantiatedScripts = new List<Script>();

            string path = Variables.BasePath + "scripts\\";
            if ( Directory.Exists( path ) ) {
                foreach ( string file in Directory.GetFiles( path, "*.*", SearchOption.TopDirectoryOnly ) ) {
                    try {
                        StreamReader reader = new StreamReader( file, System.Text.Encoding.Default );
                        string src = reader.ReadToEnd();
                        reader.Close();
                        string guid = Path.GetFileName( file );
                        bool enabled = false;
                        DateTime t = DateTime.Now;

                        string status = Variables.BasePath + "scripts\\settings\\status.dat";
                        if ( File.Exists( status ) ) {
                            string line;
                            StreamReader r = new StreamReader( Variables.BasePath + "scripts\\settings\\status.dat", System.Text.Encoding.Default );
                            while ( ( line = r.ReadLine() ) != null ) {
                                string[] tmp = line.Split( ',' );
                                if ( tmp[0].Equals( guid ) ) {
                                    enabled = Boolean.Parse( tmp[1] );
                                    t = DateTime.Parse( tmp[2] );
                                }
                            }
                            r.Close();
                        }

                        Script script = new Script( src, file, ScriptParser.ParseScriptDescription( src ), guid, enabled,t );
                        m_Scripts.Add( script );
                    } catch ( Exception ex ) {
                        Console.WriteLine( ex.Message );
                    }
                }
            }
        }

        private List<Script> m_Scripts;
        private IScriptingEvents m_Events;
        private List<Script> m_InstantiatedScripts;

        public List<Script> Scripts {
            get { return m_Scripts; }
        }

        public Script GetScriptByGuid( string guid ) {
            foreach ( Script s in m_Scripts ) {
                if ( s.Guid.Equals( guid ) ) {
                    return s;
                }
            }

            return null;
        }

        public void DeleteScriptFromFileSystemByGuid( string guid ) {
            List<string> FilesToDelete = new List<string>();
            string path = Variables.BasePath + "scripts\\";
            if ( Directory.Exists( path ) ) {
                foreach ( string file in Directory.GetFiles( path, "*.*", SearchOption.TopDirectoryOnly ) ) {
                    try {
                        StreamReader reader = new StreamReader( file, System.Text.Encoding.Default );
                        string src = reader.ReadToEnd();
                        reader.Close();
                        string guidtmp = Path.GetFileName( file );
                        if ( guidtmp.Equals( guid ) ) {
                            FilesToDelete.Add( file );
                        }
                    } catch ( Exception ex ) {
                        Console.WriteLine( ex.Message );
                    }
                }

                try {
                    foreach ( string f in FilesToDelete )
                        File.Delete( f );
                } catch ( Exception ex ) {
                    Console.WriteLine( ex.Message );
                }
            }
        }

        public List<string> Validate( string src) {
           
            List<string> errors = new List<string>();
            Dictionary<string, string> options = new Dictionary<string, string> { { "CompilerVersion", "v4.0" } };
            CSharpCodeProvider provider = new CSharpCodeProvider( options );
            CompilerParameters cparams = new CompilerParameters { GenerateExecutable = false, GenerateInMemory = true };

            Assembly ExecutingAssembly = Assembly.GetExecutingAssembly();

            cparams.ReferencedAssemblies.Add( "mscorlib.dll" );
            cparams.ReferencedAssemblies.Add( "System.dll" );
            cparams.ReferencedAssemblies.Add( "System.Data.dll" );
            cparams.ReferencedAssemblies.Add( "System.Drawing.dll" );
            cparams.ReferencedAssemblies.Add( "System.Windows.Forms.dll" );

            cparams.ReferencedAssemblies.Add( ExecutingAssembly.Location );

            CompilerResults results = provider.CompileAssemblyFromSource( cparams, src);

            if ( results.Errors.HasErrors ) {
                foreach ( CompilerError error in results.Errors ) {
                    string err;
                    err = "[" + error.ErrorNumber + "] " + error.ErrorText + " in file " + error.FileName +
                        " at line " + error.Line + Environment.NewLine;
                    errors.Add( err );
                }
            }

            return errors;
        }

        private bool IsScriptInstantiated(Script script) {
            foreach ( Script s in m_InstantiatedScripts ) {
                if ( script.Description.Equals( s.Description ) ) {
                    MessageBox.Show( "A Script with the same description is already active. You might want to change the description of the script.",
                        "Script already active", MessageBoxButtons.OK );
                    this.GetScriptByGuid( script.Guid ).Enabled = false;
                    return true;
                }
            }

            return false;
        }

        public List<string> Instantiate( Script script ) {
            //This is called when user activates a script
            //Handle compiler stuff here
            List<string> errors = new List<string>();
            if ( !IsScriptInstantiated( script ) ) {
                Dictionary<string, string> options = new Dictionary<string, string> { { "CompilerVersion", "v4.0" } };
                CSharpCodeProvider provider = new CSharpCodeProvider( options );
                CompilerParameters cparams = new CompilerParameters { GenerateExecutable = false, GenerateInMemory = true };

                Assembly ExecutingAssembly = Assembly.GetExecutingAssembly();

                cparams.ReferencedAssemblies.Add( "mscorlib.dll" );
                cparams.ReferencedAssemblies.Add( "System.dll" );
                cparams.ReferencedAssemblies.Add( "System.Data.dll" );
                cparams.ReferencedAssemblies.Add( "System.Drawing.dll" );
                cparams.ReferencedAssemblies.Add( "System.Windows.Forms.dll" );

                cparams.ReferencedAssemblies.Add( ExecutingAssembly.Location );

                CompilerResults results = provider.CompileAssemblyFromSource( cparams, script.Source );
                string temp = Path.GetTempFileName();

                if ( results.Errors.HasErrors ) {
                    foreach ( CompilerError error in results.Errors ) {
                        string err;
                        err = "[" + error.ErrorNumber + "] " + error.ErrorText + " in file " + error.FileName +
                            " at line " + error.Line + Environment.NewLine;
                        errors.Add( err );
                    }
                } else {
                    try {
                        object obj = results.CompiledAssembly.CreateInstance( "NewsWormScript" );

                        MethodInfo mi = obj.GetType().GetMethod( "Activate" );
                        Object[] InvokeParams = new Object[1] { m_Events };
                        mi.Invoke( obj, InvokeParams );
                        m_InstantiatedScripts.Add( script );
                    } catch ( Exception ex ) {
                        errors.Add( ex.StackTrace + Environment.NewLine );
                    }
                }

            }

            return errors;
        }
    }
}
